package cn.lg.soar.mvc.handler;

import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.util.jackson.JacksonUtil;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 统一数据格式
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@ControllerAdvice(annotations = RestController.class)
public class SoarResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private final static Class<?> voidClass = void.class;

    @Override
    public Object beforeBodyWrite(
            Object o,
            MethodParameter methodParameter,
            MediaType mediaType,
            Class aClass,
            ServerHttpRequest request,
            ServerHttpResponse response
    ) {
        // 调用方通过请求头的”soar-response-pack-mode“控制是否包装返回
        String mode = request.getHeaders().getFirst("soar-response-pack-mode");
        if ("pack".equals(mode)) {
            // 已包装的无需处理
            if (o instanceof HttpResult) {
                return o;
            }
            // 字符串包装处理
            if (o instanceof String) {
                return JacksonUtil.toJson(HttpResult.data(o));
            }
            // 无返回值的处理
            if (o == null && voidClass.equals(methodParameter.getParameterType())) {
                return HttpResult.success();
            }
            // 其他类型包装处理
            return HttpResult.data(o);
        }
        if ("unpack".equals(mode)) {
            // 已包装的，拆包处理
            if (o instanceof HttpResult) {
                return ((HttpResult<?>) o).getData();
            }
        }
        // 不处理
        return o;
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

}
